// RPGGame.cpp : Defines the entry point for the console application.
//

#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

#define NOMINMAX
#include <windows.h>
#include <GL\gl.h>
#include <GL\glut.h>
#include <cg/cg.h>
#include <Cg/cgGL.h>
#include <fmod.hpp>
#include <fmod_errors.h>
#include <FreeImage.h>
#include <iostream>
#include "model.h"
#include "tick.h"
#include "imageloader.h"
#undef random
#include <NxPhysics.h>
#include "MyContactReport.h"

#define GL_CLAMP_TO_EDGE 0x812F
#define StepSize 1.0f;
#define	MAX_PARTICLES 1000
#define MAX_CHAR 128

static CGcontext   myCgContext;
static CGprofile   myCgVertexProfile;
static CGprofile   myCgFragmentProfile;
static CGprogram   myCgVertexProgram;
static CGprogram   myCgFragmentProgram;
static const char *myProgramName = "Hello CG";
static const char *myVertexProgramFileName = "vs.cg";
static const char *myVertexProgramName = "vs_main";
static const char *myFragmentProgramFileName = "fs.cg";
static const char *myFragmentProgramName = "fs_main";

static bool             gPause = false;
static NxPhysicsSDK*    gPhysicsSDK = NULL;
static NxScene*         gScene = NULL;
static NxVec3           gDefaultGravity(0.0f, -9.8f, 0.0f);
static NxUserContactReport* myContactReport = NULL;
static float            gRatio=1.0f;

bool win = false;
bool processedLod = false;
int energy = 3;

typedef struct						// Create A Structure For Particle
{
	bool	active;					// Active (Yes/No)
	float	life;					// Particle Life
	float	fade;					// Fade Speed
	float	r;						// Red Value
	float	g;						// Green Value
	float	b;						// Blue Value
	float	x;						// X Position
	float	y;						// Y Position
	float	z;						// Z Position
	float	xi;						// X Direction
	float	yi;						// Y Direction
	float	zi;						// Z Direction
	float	xg;						// X Gravity
	float	yg;						// Y Gravity
	float	zg;						// Z Gravity
}
particles;							// Particles Structure

particles particle[MAX_PARTICLES];	// Particle Array (Room For Particle Info)

static GLfloat colors[12][3]=		// Rainbow Of Colors
{
	{1.0f,0.5f,0.5f},{1.0f,0.75f,0.5f},{1.0f,1.0f,0.5f},{0.75f,1.0f,0.5f},
	{0.5f,1.0f,0.5f},{0.5f,1.0f,0.75f},{0.5f,1.0f,1.0f},{0.5f,0.75f,1.0f},
	{0.5f,0.5f,1.0f},{0.75f,0.5f,1.0f},{1.0f,0.5f,1.0f},{1.0f,0.5f,0.75f}
};

Image *image; // terrain grey image

Model *pModelChar;
Model *pModelSkel;

bool rainbow = true;
float slowdown = 2.0f;				// Slow Down Particles
float xspeed;						// Base X Speed (To Allow Keyboard Direction Of Tail)
float yspeed;						// Base Y Speed (To Allow Keyboard Direction Of Tail)
float zoom = -40.0f;				// Used To Zoom Out

GLuint loop;						// Misc Loop Variable
GLuint col;						// Current Color Selection
GLuint delay;						// Rainbow Effect Delay
GLfloat (*normals)[3];

// lighting parameters
const GLfloat light0_ambient[] =  {0.1f, 0.1f, 0.3f, 1.0f};
//const GLfloat light0_diffuse[] =  {0.6f, 0.6f, 1.0f, 1.0f};
const GLfloat light0_diffuse[] =  {1.0f, 1.0f, 1.0f, 1.0f};
const GLfloat light0_position[] = {1.0f, 1.0f, 1.0f, 0.0f};
const GLfloat global_ambient[] = {0.2f, 0.2f, 0.2f, 1.0f};

const GLfloat ambient[] = {0.2f, 0.2f, 0.2f, 1.0f};
const GLfloat diffuse[] = {0.8f, 0.2f, 0.5f, 1.0f};
const GLfloat specular[] = {0.5f, 0.5f, 0.5f, 1.0f};
const GLfloat shininess[] = {100.0};

float eyeX = 0.0f; // eyeX
float eyeY = 100.0f; // eyeY
float eyeZ = 1.0f; // eyeZ
float centerX = 0.0f; // centerX
float centerY = 100.0f; // centerY
float centerZ = 0.0f; // centerZ
float skeletonX = 0.0f;
float skeletonY = 50.0f;
float skeletonZ = 200.0f;

float leftRightRotation = 0;

unsigned int lastTick = Tick::getTick();

GLuint particleTex;
GLuint crateTex;

static GLuint texArr[6];

enum direction{FRONT, BACK, RIGHT, LEFT, UP, DOWN};

bool should[260][260];

struct Point {
	int x, y;
	Point(int x,int y) {
		this->x = x;
		this->y = y;
	}
	Point operator +(Point other) {
		return Point(x+other.x,y+other.y);
	}
	Point operator/(int d) {
		return Point(x / d, y / d);
	}
};
int thres = 1000;

void cgDisplay()
{
	static float angle;
	glRotatef(angle, 0.0,1.0,0.0);
	cgGLBindProgram(myCgVertexProgram);
	cgGLEnableProfile(myCgVertexProfile);
	cgGLBindProgram(myCgFragmentProgram);
	cgGLEnableProfile(myCgFragmentProfile);
	CGparameter mvp = cgGetNamedParameter(myCgVertexProgram, "MVP");
	cgGLSetStateMatrixParameter(mvp, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);
	glutSolidTorus(0.3,1.0,30,30);
	//glutSolidCube(3, 30, 30);
	cgGLDisableProfile(myCgVertexProfile);
	cgGLDisableProfile(myCgFragmentProfile);
	//angle += 0.5;
	angle += 6.0;
	if(angle >=360) angle = 0.0f;
}

void getCrossProduct(float x1, float y1, float z1, float x2, float y2, float z2, float normal[3]) // calculate cross product
{
	normal[0] = y1 * z2 - y2 * z1;
	normal[1] = x2 * z1 - x1 * z2;
	normal[2] = x1 * y2 - x2 * y1;
}

int getHeight(int i, int j) // get height from a gray image
{	
	unsigned char red = image->pixels[3 * ((image->height - j - 1) * image->width + i)]; // get red
	unsigned char green = image->pixels[3 * ((image->height - j - 1) * image->width + i) + 1]; // get green
	unsigned char blue = image->pixels[3 * ((image->height - j - 1) * image->width + i) + 2]; // get blue

	int grey =  0.299 * int(red) + 0.587 * int(green) + 0.114 * int(blue); // calculating formula
	return grey;
}

void getNormal(int i, int j, float normal[3]) // get normal for the terrain
{
	GLfloat normal1[3], normal2[3], normal3[3], normal4[3];
	GLfloat x[4], y[4], z[4];
	GLfloat xIncrement = 1.0f / image->width * 2;
	GLfloat zIncrement = 1.0f / image->height * 2;
	if ((i < image->height - 1) && (i > 0) && (j > 0) && (j < image->width - 1))
		// in the middle
	{
		x[0] = -xIncrement;
		y[0] = double(getHeight(i - 1, j) - getHeight(i, j)) / 255 / 5;
		z[0] = 0;
		x[1] = 0;
		y[1] = double(getHeight(i, j + 1) - getHeight(i, j)) / 255 / 5;
		z[1] = zIncrement;
		x[2] = xIncrement;
		y[2] = double(getHeight(i + 1, j) - getHeight(i, j)) / 255 / 5;
		z[2] = 0;
		x[3] = 0;
		y[3] = double(getHeight(i, j - 1) - getHeight(i, j)) / 255 / 5;
		z[3] = -zIncrement;
		// get four normals
		getCrossProduct(x[0], y[0], z[0], x[1], y[1], z[1], normal1);
		getCrossProduct(x[1], y[1], z[1], x[2], y[2], z[2], normal2);
		getCrossProduct(x[2], y[2], z[2], x[3], y[3], z[3], normal3);
		getCrossProduct(x[3], y[3], z[3], x[0], y[0], z[0], normal4);
		normal[0] = normal1[0] + normal2[0] + normal3[0] + normal4[0];
		normal[1] = normal1[1] + normal2[1] + normal3[1] + normal4[1];
		normal[2] = normal1[2] + normal2[2] + normal3[2] + normal4[2];
		// calculate mean value
		float norm = sqrt(normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]);
		normal[0] /= norm;
		normal[1] /= norm;
		normal[2] /= norm;
		return;
	}
	// on the edge
	if ((i == 0) && (j == 0))
	{
		x[0] = 0;
		y[0] = double(getHeight(i, j + 1) - getHeight(i, j)) / 255 / 5;
		z[0] = zIncrement;
		x[1] = xIncrement;
		y[1] = double(getHeight(i + 1, j) - getHeight(i, j)) / 255 / 5;
		z[1] = 0;
		getCrossProduct(x[0], y[0], z[0], x[1], y[1], z[1], normal1);
		normal[0] = normal1[0];
		normal[1] = normal1[1];
		normal[2] = normal1[2];
		float norm = sqrt(normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]);
		normal[0] /= norm;
		normal[1] /= norm;
		normal[2] /= norm;
		return;
	}
	// on the edge
	if ((i == 0) && (j == image->height - 1))
	{
		x[0] = xIncrement;
		y[0] = double(getHeight(i + 1, j) - getHeight(i, j)) / 255 / 5;
		z[0] = 0;
		x[1] = 0;
		y[1] = double(getHeight(i, j - 1) - getHeight(i, j)) / 255 / 5;
		z[1] = -zIncrement;
		getCrossProduct(x[0], y[0], z[0], x[1], y[1], z[1], normal1);
		normal[0] = normal1[0];
		normal[1] = normal1[1];
		normal[2] = normal1[2];
		float norm = sqrt(normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]);
		normal[0] /= norm;
		normal[1] /= norm;
		normal[2] /= norm;
		return;
	}
	// on the edge
	if ((i == image->width - 1) && (j == 0))
	{
		x[0] = -xIncrement;
		y[0] = double(getHeight(i - 1, j) - getHeight(i, j)) / 255 / 5;
		z[0] = 0;
		x[1] = 0;
		y[1] = double(getHeight(i, j + 1) - getHeight(i, j)) / 255 / 5;
		z[1] = zIncrement;
		getCrossProduct(x[0], y[0], z[0], x[1], y[1], z[1], normal1);
		normal[0] = normal1[0];
		normal[1] = normal1[1];
		normal[2] = normal1[2];
		float norm = sqrt(normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]);
		normal[0] /= norm;
		normal[1] /= norm;
		normal[2] /= norm;
		return;
	}
	// on the edge
	if ((i == image->width - 1) && (j == image->height - 1))
	{
		x[0] = 0;
		y[0] = double(getHeight(i, j - 1) - getHeight(i, j)) / 255 / 5;
		z[0] = -zIncrement;
		x[1] = -xIncrement;
		y[1] = double(getHeight(i - 1, j) - getHeight(i, j)) / 255 / 5;
		z[1] = 0;
		getCrossProduct(x[0], y[0], z[0], x[1], y[1], z[1], normal1);
		normal[0] = normal1[0];
		normal[1] = normal1[1];
		normal[2] = normal1[2];
		float norm = sqrt(normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]);
		normal[0] /= norm;
		normal[1] /= norm;
		normal[2] /= norm;
		return;
	}
	// on the x axis
	if (i == 0)
	{
		x[0] = 0;
		y[0] = double(getHeight(i, j + 1) - getHeight(i, j)) / 255 / 5;
		z[0] = zIncrement;
		x[1] = xIncrement;
		y[1] = double(getHeight(i + 1, j) - getHeight(i, j)) / 255 / 5;
		z[1] = 0;
		x[2] = 0;
		y[2] = double(getHeight(i, j - 1) - getHeight(i, j)) / 255 / 5;
		z[2] = -zIncrement;
		getCrossProduct(x[0], y[0], z[0], x[1], y[1], z[1], normal1);
		getCrossProduct(x[1], y[1], z[1], x[2], y[2], z[2], normal2);
		normal[0] = normal1[0] + normal2[0];
		normal[1] = normal1[1] + normal2[1];
		normal[2] = normal1[2] + normal2[2];
		float norm = sqrt(normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]);
		normal[0] /= norm;
		normal[1] /= norm;
		normal[2] /= norm;
		return;
	}
	// on the x axis
	if (i == image->width - 1)
	{
		x[0] = 0;
		y[0] = double(getHeight(i, j - 1) - getHeight(i, j)) / 255 / 5;
		z[0] = -zIncrement;
		x[1] = -xIncrement;
		y[1] = double(getHeight(i - 1, j) - getHeight(i, j)) / 255 / 5;
		z[1] = 0;
		x[2] = 0;
		y[2] = double(getHeight(i, j + 1) - getHeight(i, j)) / 255 / 5;
		z[2] = zIncrement;
		getCrossProduct(x[0], y[0], z[0], x[1], y[1], z[1], normal1);
		getCrossProduct(x[1], y[1], z[1], x[2], y[2], z[2], normal2);
		normal[0] = normal1[0] + normal2[0];
		normal[1] = normal1[1] + normal2[1];
		normal[2] = normal1[2] + normal2[2];
		float norm = sqrt(normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]);
		normal[0] /= norm;
		normal[1] /= norm;
		normal[2] /= norm;
		return;
	}
	// on the y axis
	if (j == 0)
	{
		x[0] = -xIncrement;
		y[0] = double(getHeight(i - 1, j) - getHeight(i, j)) / 255 / 5;
		z[0] = 0;
		x[1] = 0;
		y[1] = double(getHeight(i, j + 1) - getHeight(i, j)) / 255 / 5;
		z[1] = zIncrement;
		x[2] = xIncrement;
		y[2] = double(getHeight(i + 1, j) - getHeight(i, j)) / 255 / 5;
		z[2] = 0;
		getCrossProduct(x[0], y[0], z[0], x[1], y[1], z[1], normal1);
		getCrossProduct(x[1], y[1], z[1], x[2], y[2], z[2], normal2);
		normal[0] = normal1[0] + normal2[0];
		normal[1] = normal1[1] + normal2[1];
		normal[2] = normal1[2] + normal2[2];
		float norm = sqrt(normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]);
		normal[0] /= norm;
		normal[1] /= norm;
		normal[2] /= norm;
		return;
	}
	// on the y axis
	if (j == image->height - 1)
	{
		x[0] = xIncrement;
		y[0] = double(getHeight(i + 1, j) - getHeight(i, j)) / 255 / 5;
		z[0] = 0;
		x[1] = 0;
		y[1] = double(getHeight(i, j - 1) - getHeight(i, j)) / 255 / 5;
		z[1] = -zIncrement;
		x[2] = -xIncrement;
		y[2] = double(getHeight(i - 1, j) - getHeight(i, j)) / 255 / 5;
		z[2] = 0;
		getCrossProduct(x[0], y[0], z[0], x[1], y[1], z[1], normal1);
		getCrossProduct(x[1], y[1], z[1], x[2], y[2], z[2], normal2);
		normal[0] = normal1[0] + normal2[0];
		normal[1] = normal1[1] + normal2[1];
		normal[2] = normal1[2] + normal2[2];
		float norm = sqrt(normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]);
		normal[0] /= norm;
		normal[1] /= norm;
		normal[2] /= norm;
		return;
	}
}

void getAvgNormal(int i, int j, GLfloat normal[3]) // get average normal
{
	// get four normals and itself do average
	GLfloat normal1[3], normal2[3], normal3[3], normal4[3];
	normal[0] = normals[j * image->width + i][0];
	normal[1] = normals[j * image->width + i][1];
	normal[2] = normals[j * image->width + i][2];
	if ((i > 0) && (j > 0) && (i < image->width - 1) && (j < image->height - 1))
	{
		normal1[0] = normals[j * image->width + i - 1][0];
		normal1[1] = normals[j * image->width + i - 1][1];
		normal1[2] = normals[j * image->width + i - 1][2];
		normal2[0] = normals[(j + 1) * image->width + i][0];
		normal2[1] = normals[(j + 1) * image->width + i][1];
		normal2[2] = normals[(j + 1) * image->width + i][2];
		normal3[0] = normals[j * image->width + i + 1][0];
		normal3[1] = normals[j * image->width + i + 1][1];
		normal3[2] = normals[j * image->width + i + 1][2];
		normal4[0] = normals[(j - 1) * image->width + i][0];
		normal4[1] = normals[(j - 1) * image->width + i][1];
		normal4[2] = normals[(j - 1) * image->width + i][2];
		// calculate x
		normal[0] = (normal1[0] * 0.5 + normal2[0] * 0.5 + normal3[0] * 0.5 + normal4[0] * 0.5 + normal[0]) / 3;
		// calculate y
		normal[1] = (normal1[1] * 0.5 + normal2[1] * 0.5 + normal3[1] * 0.5 + normal4[1] * 0.5 + normal[1]) / 3;
		// calculate z
		normal[2] = (normal1[2] * 0.5 + normal2[2] * 0.5 + normal3[2] * 0.5 + normal4[2] * 0.5 + normal[2]) / 3;
		return;
	}
	// in the corner only 3
	if ((i == 0) && (j == 0))
	{
		normal1[0] = normals[(j + 1) * image->width + i][0];
		normal1[1] = normals[(j + 1) * image->width + i][1];
		normal1[2] = normals[(j + 1) * image->width + i][2];
		normal2[0] = normals[j * image->width + i + 1][0];
		normal2[1] = normals[j * image->width + i + 1][1];
		normal2[2] = normals[j * image->width + i + 1][2];
		// calculate x
		normal[0] = (normal1[0] * 0.5 + normal2[0] * 0.5 + normal[0]) / 2;
		// calculate y
		normal[1] = (normal1[1] * 0.5 + normal2[1] * 0.5 + normal[1]) / 2;
		// calculate z
		normal[2] = (normal1[2] * 0.5 + normal2[2] * 0.5 + normal[2]) / 2;
		return;
	}
	// in the corner only 3
	if ((i == 0) && (j == image->height - 1))
	{
		normal1[0] = normals[j * image->width + i + 1][0];
		normal1[1] = normals[j * image->width + i + 1][1];
		normal1[2] = normals[j * image->width + i + 1][2];
		normal2[0] = normals[(j - 1) * image->width + i][0];
		normal2[1] = normals[(j - 1) * image->width + i][1];
		normal2[2] = normals[(j - 1) * image->width + i][2];
		// calculate x
		normal[0] = (normal1[0] * 0.5 + normal2[0] * 0.5 + normal[0]) / 2;
		// calculate y
		normal[1] = (normal1[1] * 0.5 + normal2[1] * 0.5 + normal[1]) / 2;
		// calculate z
		normal[2] = (normal1[2] * 0.5 + normal2[2] * 0.5 + normal[2]) / 2;
		return;
	}
	// in the corner only 3
	if ((i == image->width - 1) && (j == 0))
	{
		normal1[0] = normals[j * image->width + i - 1][0];
		normal1[1] = normals[j * image->width + i - 1][1];
		normal1[2] = normals[j * image->width + i - 1][2];
		normal2[0] = normals[(j + 1) * image->width + i][0];
		normal2[1] = normals[(j + 1) * image->width + i][1];
		normal2[2] = normals[(j + 1) * image->width + i][2];
		// calculate x
		normal[0] = (normal1[0] * 0.5 + normal2[0] * 0.5 + normal[0]) / 2;
		// calculate y
		normal[1] = (normal1[1] * 0.5 + normal2[1] * 0.5 + normal[1]) / 2;
		// calculate z
		normal[2] = (normal1[2] * 0.5 + normal2[2] * 0.5 + normal[2]) / 2;
		return;
	}
	// in the corner only 3
	if ((i == image->width -1) && (j == image->height - 1))
	{
		normal1[0] = normals[j * image->width + i - 1][0];
		normal1[1] = normals[j * image->width + i - 1][1];
		normal1[2] = normals[j * image->width + i - 1][2];
		normal2[0] = normals[(j - 1) * image->width + i][0];
		normal2[1] = normals[(j - 1) * image->width + i][1];
		normal2[2] = normals[(j - 1) * image->width + i][2];
		// calculate x
		normal[0] = (normal1[0] * 0.5 + normal2[0] * 0.5 + normal[0]) / 2;
		// calculate y
		normal[1] = (normal1[1] * 0.5 + normal2[1] * 0.5 + normal[1]) / 2;
		// calculate z
		normal[2] = (normal1[2] * 0.5 + normal2[2] * 0.5 + normal[2]) / 2;
		return;
	}
	// on the side, 4 taking into account
	if (i == 0)
	{
		normal1[0] = normals[(j + 1) * image->width + i][0];
		normal1[1] = normals[(j + 1) * image->width + i][1];
		normal1[2] = normals[(j + 1) * image->width + i][2];
		normal2[0] = normals[j * image->width + i + 1][0];
		normal2[1] = normals[j * image->width + i + 1][1];
		normal2[2] = normals[j * image->width + i + 1][2];
		normal3[0] = normals[(j - 1) * image->width + i][0];
		normal3[1] = normals[(j - 1) * image->width + i][1];
		normal3[2] = normals[(j - 1) * image->width + i][2];
		// calculate x
		normal[0] = (normal1[0] * 0.5 + normal2[0] * 0.5 + normal3[0] * 0.5 + normal[0]) / 2.5;
		// calculate y
		normal[1] = (normal1[1] * 0.5 + normal2[1] * 0.5 + normal3[1] * 0.5 + normal[1]) / 2.5;
		// calculate z
		normal[2] = (normal1[2] * 0.5 + normal2[2] * 0.5 + normal3[2] * 0.5 + normal[2]) / 2.5;
		return;
	}
	// on the side, 4 taking into account
	if (i == image->width - 1)
	{
		normal1[0] = normals[(j - 1) * image->width + i][0];
		normal1[1] = normals[(j - 1) * image->width + i][1];
		normal1[2] = normals[(j - 1) * image->width + i][2];
		normal2[0] = normals[j * image->width + i - 1][0];
		normal2[1] = normals[j * image->width + i - 1][1];
		normal2[2] = normals[j * image->width + i - 1][2];
		normal3[0] = normals[(j + 1) * image->width + i][0];
		normal3[1] = normals[(j + 1) * image->width + i][1];
		normal3[2] = normals[(j + 1) * image->width + i][2];
		// calculate x
		normal[0] = (normal1[0] * 0.5 + normal2[0] * 0.5 + normal3[0] * 0.5 + normal[0]) / 2.5;
		// calculate y
		normal[1] = (normal1[1] * 0.5 + normal2[1] * 0.5 + normal3[1] * 0.5 + normal[1]) / 2.5;
		// calculate z
		normal[2] = (normal1[2] * 0.5 + normal2[2] * 0.5 + normal3[2] * 0.5 + normal[2]) / 2.5;
		return;
	}
	// on the side, 4 taking into account
	if (j == 0)
	{
		normal1[0] = normals[j * image->width + i - 1][0];
		normal1[1] = normals[j * image->width + i - 1][1];
		normal1[2] = normals[j * image->width + i - 1][2];
		normal2[0] = normals[(j + 1) * image->width + i][0];
		normal2[1] = normals[(j + 1) * image->width + i][1];
		normal2[2] = normals[(j + 1) * image->width + i][2];
		normal3[0] = normals[j * image->width + i + 1][0];
		normal3[1] = normals[j * image->width + i + 1][1];
		normal3[2] = normals[j * image->width + i + 1][2];
		// calculate x
		normal[0] = (normal1[0] * 0.5 + normal2[0] * 0.5 + normal3[0] * 0.5 + normal[0]) / 2.5;
		// calculate y
		normal[1] = (normal1[1] * 0.5 + normal2[1] * 0.5 + normal3[1] * 0.5 + normal[1]) / 2.5;
		// calculate z
		normal[2] = (normal1[2] * 0.5 + normal2[2] * 0.5 + normal3[2] * 0.5 + normal[2]) / 2.5;
		return;
	}
	// on the side, 4 taking into account
	if (j == image->height - 1)
	{
		normal1[0] = normals[j * image->width + i + 1][0];
		normal1[1] = normals[j * image->width + i + 1][1];
		normal1[2] = normals[j * image->width + i + 1][2];
		normal2[0] = normals[(j - 1) * image->width + i][0];
		normal2[1] = normals[(j - 1) * image->width + i][1];
		normal2[2] = normals[(j - 1) * image->width + i][2];
		normal3[0] = normals[j * image->width + i - 1][0];
		normal3[1] = normals[j * image->width + i - 1][1];
		normal3[2] = normals[j * image->width + i - 1][2];
		// calculate x
		normal[0] = (normal1[0] * 0.5 + normal2[0] * 0.5 + normal3[0] * 0.5 + normal[0]) / 2.5;
		// calculate y
		normal[1] = (normal1[1] * 0.5 + normal2[1] * 0.5 + normal3[1] * 0.5 + normal[1]) / 2.5;
		// calculate z
		normal[2] = (normal1[2] * 0.5 + normal2[2] * 0.5 + normal3[2] * 0.5 + normal[2]) / 2.5;
		return;
	}
}

void createSkybox(GLuint texArr[], char *filename, int ID) // create skybox
{
	FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
	fif = FreeImage_GetFileType(filename, 0);
	if(fif == FIF_UNKNOWN) 
		fif = FreeImage_GetFIFFromFilename(filename);
	FIBITMAP *dib = NULL;
	if(FreeImage_FIFSupportsReading(fif))
		dib = FreeImage_Load(fif, filename);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 8);
	glGenTextures(1, &texArr[ID]);
	glBindTexture(GL_TEXTURE_2D, texArr[ID]);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); //defined in opengl 1.3
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
	gluBuild2DMipmaps(GL_TEXTURE_2D, 3, FreeImage_GetWidth(dib), FreeImage_GetHeight(dib), GL_BGR_EXT, GL_UNSIGNED_BYTE, FreeImage_GetBits(dib));
	// Free FreeImage's copy of the data
	FreeImage_Unload(dib);
}

void createParticleTex(char *filename)
{
	FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
	fif = FreeImage_GetFileType(filename, 0);
	if(fif == FIF_UNKNOWN) 
		fif = FreeImage_GetFIFFromFilename(filename);
	FIBITMAP *dib = NULL;
	if(FreeImage_FIFSupportsReading(fif))
		dib = FreeImage_Load(fif, filename);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 8);
	glGenTextures(1, &particleTex);
	glBindTexture(GL_TEXTURE_2D, particleTex);

	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexImage2D(GL_TEXTURE_2D, 0, 3, FreeImage_GetWidth(dib), FreeImage_GetHeight(dib), 0, GL_RGB, GL_UNSIGNED_BYTE, FreeImage_GetBits(dib));
}

static void render(Point pt)
{
	glTexCoord2f(GLfloat(pt.x) / GLfloat(image->width - 1), GLfloat(image->height - pt.y - 1) / GLfloat(image->height - 1));
	glVertex3f(pt.x, double(getHeight(pt.x, image->height - pt.y - 1)) / 255 / 5 * 1000 - 1, pt.y);
}

static void renderRecur(Point lu, Point ld, Point rd, Point ru)
{
	Point mid((lu.x + ru.x) / 2, (lu.y + ld.y) / 2);
	Point lm = (lu + ld) / 2, dm = (ld + rd) / 2, rm = (ru + rd) / 2, um = (lu + ru) / 2;
	int w = (ru.x - lu.x);
	if (w != 0)
		w = ld.y - lu.y;
	if (w == 0)
		return;
	else if (should[mid.y][mid.x] && w != 1)
	{
		renderRecur(lu, lm, mid, um);
		renderRecur(lm, ld, dm, mid);
		renderRecur(mid, dm, rd, rm);
		renderRecur(um, mid, rm, ru);
	}
	else
	{
		glBegin(GL_TRIANGLE_FAN);
		{
			render(mid);
			int x, y;
			x = lu.x;
			for(y = lu.y; y <= ld.y; y++)
				if (should[y][x])
					render(Point(x, y));
			y = ld.y;
			for(x = ld.x; x <= rd.x; x++)
				if (should[y][x])
					render(Point(x, y));
			x = rd.x;
			for(y = rd.y; y >= ru.y; y--)
				if (should[y][x])
					render(Point(x, y));
			y=ru.y;
			for(x = ru.x; x >= lu.x; x--)
				if (should[y][x])
					render(Point(x, y));
		}
		glEnd();
	}
}

void setRecur(Point lu, Point ld, Point rd, Point ru)
{
	should[lu.y][lu.x]=true;
	should[ld.y][ld.x]=true;
	should[rd.y][rd.x]=true;
	should[ru.y][ru.x]=true;
	int w = -lu.x + ru.x;
	float dis = sqrt((lu.x * 2000.0 / image->width - 1000 - eyeX) * (lu.x * 2000.0 / image->width - 1000 - eyeX) +
		(double(getHeight(lu.x, image->height - lu.y - 1)) / 255 / 5 * 1000 - eyeY) *
		(double(getHeight(lu.x, image->height - lu.y - 1)) / 255 / 5 * 1000 - eyeY) +
		(lu.y * 2000.0 / image->height - 1000 - eyeZ) * (lu.y * 2000.0 / image->height - 1000 - eyeZ));
	if (lu.y == ld.y ||lu.x == ru.x) {
		return;
	} else if (abs(lu.y - ld.y) == 1 || abs(lu.x - ru.x)==1 || (abs(lu.x - ru.x) / dis <= .015)) {
		return;
	} else {
		Point mid((lu.x + ru.x) / 2,(lu.y + ld.y) / 2);
		Point lm = (lu + ld) / 2, dm = (ld + rd) / 2, rm = (ru + rd) / 2,um = (lu + ru) / 2;
		setRecur(lu, lm, mid, um);
		setRecur(lm, ld, dm, mid);
		setRecur(mid, dm, rd, rm);
		setRecur(um, mid, rm, ru);
	}
}

void drawTerrain()
{
	glBindTexture(GL_TEXTURE_2D, texArr[DOWN]);
	memset(should, 0, sizeof should);
	setRecur(Point(0, 0), Point(0, image->height - 1), Point(image->width - 1, image->height - 1), Point(image->width - 1, 0));
	renderRecur(Point(0, 0), Point(0, image->height - 1), Point(image->width - 1, image->height - 1), Point(image->width - 1, 0));
	glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
}

void drawSkybox()
{
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, texArr[FRONT]);
	glBegin(GL_QUADS);
	glTexCoord2f(0.0, 0.0);
	glVertex3f(-1000.0, -1000.0, -1000.0);
	glTexCoord2f(1.0, 0.0);
	glVertex3f(1000.0, -1000.0, -1000.0);
	glTexCoord2f(1.0, 1.0);
	glVertex3f(1000.0, 1000.0, -1000.0);
	glTexCoord2f(0.0, 1.0);
	glVertex3f(-1000.0, 1000.0, -1000.0);
	glEnd();
	glBindTexture(GL_TEXTURE_2D, texArr[BACK]);
	glBegin(GL_QUADS);
	glTexCoord2f(1.0, 0.0);
	glVertex3f(-1000.0, -1000.0, 1000.0);
	glTexCoord2f(1.0, 1.0);
	glVertex3f(-1000.0, 1000.0, 1000.0);
	glTexCoord2f(0.0, 1.0);
	glVertex3f(1000.0, 1000.0, 1000.0);
	glTexCoord2f(0.0, 0.0);
	glVertex3f(1000.0, -1000.0, 1000.0);
	glEnd();
	glBindTexture(GL_TEXTURE_2D, texArr[LEFT]);
	glBegin(GL_QUADS);
	glTexCoord2f(1.0, 0.0);
	glVertex3f(-1000.0, -1000.0, -1000.0);
	glTexCoord2f(0.0, 0.0);
	glVertex3f(-1000.0, -1000.0, 1000.0);
	glTexCoord2f(0.0, 1.0);
	glVertex3f(-1000.0, 1000.0, 1000.0);
	glTexCoord2f(1.0, 1.0);
	glVertex3f(-1000.0, 1000.0, -1000.0);
	glEnd();
	glBindTexture(GL_TEXTURE_2D, texArr[RIGHT]);
	glBegin(GL_QUADS);
	glTexCoord2f(0.0, 0.0);
	glVertex3f(1000.0, -1000.0, -1000.0);
	glTexCoord2f(1.0, 0.0);
	glVertex3f(1000.0, -1000.0, 1000.0);
	glTexCoord2f(1.0, 1.0);
	glVertex3f(1000.0, 1000.0, 1000.0);
	glTexCoord2f(0.0, 1.0);
	glVertex3f(1000.0, 1000.0, -1000.0);
	glEnd();
	glBindTexture(GL_TEXTURE_2D, texArr[UP]);
	glBegin(GL_QUADS);
	glTexCoord2f(1.0, 0.0);
	glVertex3f(-1000.0, 1000.0, -1000.0);
	glTexCoord2f(0.0, 0.0);
	glVertex3f(-1000.0, 1000.0, 1000.0);
	glTexCoord2f(0.0, 1.0);
	glVertex3f(1000.0, 1000.0, 1000.0);
	glTexCoord2f(1.0, 1.0);
	glVertex3f(1000.0, 1000.0, -1000.0);
	glEnd();

	// draw terrain
	glBindTexture(GL_TEXTURE_2D, texArr[DOWN]);
	if (processedLod == false)
	{
		glPushMatrix();
		for (int i = 0; i < image->width - 1; i++)
		{	
			for (int j = 0; j < image->height - 1; j++)
			{
				GLfloat normal[3];
				glBegin(GL_TRIANGLE_STRIP);
				glTexCoord2f(double(i) / (image->width - 1), double(j) / (image->height - 1));
				//getAvgNormal(i, j, normal);
				//glNormal3fv(normal);
				glVertex3f((double(i) / (image->width - 1) * 2 - 1.0f) * 1000, double(getHeight(i, j)) / 255 / 5 * 1000, (-double(j) / (image->height - 1) * 2 + 1.0f) * 1000);
				glTexCoord2f(double(i) / (image->width - 1), double(j + 1) / (image->height - 1));
				//getAvgNormal(i, j + 1, normal);
				//glNormal3fv(normal);
				glVertex3f((double(i) / (image->width - 1) * 2 - 1.0f) * 1000, double(getHeight(i, j + 1)) / 255 / 5 * 1000, (-double(j + 1) / (image->height - 1) * 2 + 1.0f) * 1000);
				glTexCoord2f(double(i + 1) / (image->width - 1), double(j) / (image->height - 1));
				//getAvgNormal(i + 1, j, normal);
				//glNormal3fv(normal);
				glVertex3f((double(i + 1) / (image->width - 1) * 2 - 1.0f) * 1000, double(getHeight(i + 1, j)) / 255 / 5 * 1000, (-double(j) / (image->height - 1) * 2 + 1.0f) * 1000);
				glTexCoord2f(double(i + 1) / (image->width - 1), double(j + 1) / (image->height - 1));
				//getAvgNormal(i + 1, j + 1, normal);
				//glNormal3fv(normal);
				glVertex3f((double(i + 1) / (image->width - 1) * 2 - 1.0f) * 1000, double(getHeight(i + 1, j + 1)) / 255 / 5 * 1000, (-double(j + 1) / (image->height - 1) * 2 + 1.0f) * 1000);
				glEnd();
			}
		}
		glPopMatrix();
	}
	else
	{
		glPushMatrix();
		glTranslatef(-1000.0f, 0.0f, -1000.0f);
		glScalef(2000.0f / image->width, 1.0f, 2000.0f / image->height);
		drawTerrain();
		glPopMatrix();
	}
	glDisable(GL_TEXTURE_2D);
}

void initTerrainAndSkybox()
{
	FIBITMAP *bitmap = FreeImage_Load(FIF_JPEG, "data/terrain/terrain1.jpg");
	bitmap = FreeImage_ConvertTo24Bits(bitmap);
	image = new Image((char*)FreeImage_GetBits(bitmap), FreeImage_GetWidth(bitmap), FreeImage_GetHeight(bitmap));
	normals = new GLfloat [image->width * image->height][3];
	for (int i = 0; i < image->width; i++)
	{
		for (int j = 0; j < image->height; j++)
		{
			GLfloat normal[3];
			getNormal(i, j, normal);
			normals[j * image->width + i][0] = normal[0];
			normals[j * image->width + i][1] = normal[1];
			normals[j * image->width + i][2] = normal[2];
		}
	}

	// create skybox texture
	createSkybox(texArr, "data/skybox/skybox/east.bmp", RIGHT);
	createSkybox(texArr, "data/skybox/skybox/up.bmp", UP);
	createSkybox(texArr, "data/skybox/skybox/north.bmp", FRONT);
	createSkybox(texArr, "data/skybox/skybox/west.bmp", LEFT);
	createSkybox(texArr, "data/skybox/skybox/down.bmp", DOWN);
	createSkybox(texArr, "data/skybox/skybox/south.bmp", BACK);
	createParticleTex("data/Particle.bmp");
}

void initParticle()
{
	for (loop=0;loop<MAX_PARTICLES;loop++)				// Initials All The Textures
	{
		particle[loop].active=true;								// Make All The Particles Active
		particle[loop].life=1.0f;								// Give All The Particles Full Life
		particle[loop].fade=float(rand()%100)/1000.0f+0.003f;	// Random Fade Speed
		particle[loop].r=colors[loop*(12/MAX_PARTICLES)][0];	// Select Red Rainbow Color
		particle[loop].g=colors[loop*(12/MAX_PARTICLES)][1];	// Select Red Rainbow Color
		particle[loop].b=colors[loop*(12/MAX_PARTICLES)][2];	// Select Red Rainbow Color
		particle[loop].xi=float((rand()%50)-26.0f)*10.0f;		// Random Speed On X Axis
		particle[loop].yi=float((rand()%50)-25.0f)*10.0f;		// Random Speed On Y Axis
		particle[loop].zi=float((rand()%50)-25.0f)*10.0f;		// Random Speed On Z Axis
		particle[loop].xg=0.0f;									// Set Horizontal Pull To Zero
		particle[loop].yg=-0.8f;								// Set Vertical Pull Downward
		particle[loop].zg=0.0f;									// Set Pull On Z Axis To Zero
	}
}

void initCG()
{
	myCgContext = cgCreateContext();
	cgGLSetDebugMode(CG_FALSE);
	cgSetParameterSettingMode(myCgContext, CG_DEFERRED_PARAMETER_SETTING);
	myCgVertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX);
	cgGLSetOptimalOptions(myCgVertexProfile);
	myCgVertexProgram = cgCreateProgramFromFile(myCgContext,CG_SOURCE, myVertexProgramFileName,
		myCgVertexProfile, myVertexProgramName, NULL);
	cgGLLoadProgram(myCgVertexProgram);
	myCgFragmentProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT);
	cgGLSetOptimalOptions(myCgFragmentProfile);
	myCgFragmentProgram = cgCreateProgramFromFile(myCgContext, CG_SOURCE, myFragmentProgramFileName,
		myCgFragmentProfile, myFragmentProgramName, NULL);
	cgGLLoadProgram(myCgFragmentProgram);
}

void InitNx()
{
	myContactReport = new MyContactReport();
	gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION, 0, NULL);
	if(!gPhysicsSDK) return;
	NxSceneDesc sceneDesc;
	sceneDesc.setToDefault();
	sceneDesc.gravity = gDefaultGravity;
	gScene = gPhysicsSDK->createScene(sceneDesc);
	NxMaterial * defaultMaterial = gScene->getMaterialFromIndex(0);
	defaultMaterial->setRestitution(0.9f);
	defaultMaterial->setStaticFriction(0.1f);
	defaultMaterial->setDynamicFriction(0.1f);

	NxHeightFieldDesc HeightFieldDesc;
	HeightFieldDesc.nbColumns = image->width;
	HeightFieldDesc.nbRows = image->height;
	HeightFieldDesc.samples = new NxU32[HeightFieldDesc.nbColumns * HeightFieldDesc.nbRows];
	HeightFieldDesc.sampleStride = sizeof(NxU32);
	char* currentByte = (char*)HeightFieldDesc.samples;
	for (NxU32 column = 0; column < HeightFieldDesc.nbColumns; column++)
	{
		for (NxU32 row = 0; row < HeightFieldDesc.nbRows; row++)
		{
			NxHeightFieldSample* currentSample = (NxHeightFieldSample*)currentByte;
			currentSample->height = getHeight(column, row) * 1.0 / 255 / 5 * 1000;
			currentSample->materialIndex0 = 0;
			currentSample->materialIndex1 = 0;
			currentByte += HeightFieldDesc.sampleStride;
		}
	}
	NxHeightField* HeightField = gScene->getPhysicsSDK().createHeightField(HeightFieldDesc);
	delete [] HeightFieldDesc.samples;
	NxHeightFieldShapeDesc HeightFieldShapeDesc;
	HeightFieldShapeDesc.heightField = HeightField;
	HeightFieldShapeDesc.shapeFlags	= NX_SF_FEATURE_INDICES | NX_SF_VISUALIZATION;
	HeightFieldShapeDesc.heightScale = 1.0;
	HeightFieldShapeDesc.rowScale = 2000 / NxReal(HeightFieldDesc.nbRows - 1);
	HeightFieldShapeDesc.columnScale = 2000 / NxReal(HeightFieldDesc.nbColumns - 1);
	HeightFieldShapeDesc.materialIndexHighBits = 0;
	HeightFieldShapeDesc.holeMaterial = 2;
	NxActorDesc ActorDesc;
	ActorDesc.shapes.pushBack(&HeightFieldShapeDesc);
	ActorDesc.globalPose.t = NxVec3(-1000.0f, 0.0f, -1000.0f);
	ActorDesc.name = "heightmap";
	gScene->createActor(ActorDesc);

	NxBodyDesc BodyDesc;
	BodyDesc.angularDamping = 0.5f;
	BodyDesc.linearVelocity = NxVec3(0.0f, 0.0f, 0.0f);
	NxBoxShapeDesc BoxDesc;
	BoxDesc.dimensions = NxVec3(3.0f, 3.0f, 3.0f);
	NxActorDesc BoxActorDesc;
	BoxActorDesc.shapes.pushBack(&BoxDesc);
	BoxActorDesc.body = &BodyDesc;
	BoxActorDesc.density = 0.10f;
	BoxActorDesc.globalPose.t = NxVec3(0.0, 150.0, -50.0);
	BoxActorDesc.name = "cube";
	gScene->createActor(BoxActorDesc)->userData = (void*)(3);
}

bool createModel()
{
	std::cout << "Loading 'cally' model ..." << std::endl;
	pModelChar = new Model();
	pModelChar->setPath( "data/cally/" );

	if(!pModelChar->onInit("data/cally.cfg"))
	{
		delete pModelChar;
		std::cerr << "Model initialization failed! (cally)" << std::endl;
		return false;
	}

	std::cout << "Loading 'skeleton' model ..." << std::endl;
	pModelSkel = new Model();
	pModelSkel->setPath( "data/skeleton/" );

	if(!pModelSkel->onInit("data/skeleton.cfg"))
	{
		delete pModelSkel;
		std::cerr << "Model initialization failed! (skeleton)" << std::endl;
		return false;
	}
	return true;
}

void createCrateTex(char *filename)
{
	FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
	fif = FreeImage_GetFileType(filename, 0);
	if(fif == FIF_UNKNOWN) 
		fif = FreeImage_GetFIFFromFilename(filename);
	FIBITMAP *dib = NULL;
	if(FreeImage_FIFSupportsReading(fif))
		dib = FreeImage_Load(fif, filename);
	dib = FreeImage_ConvertTo24Bits(dib);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 8);
	glGenTextures(1, &crateTex);
	glBindTexture(GL_TEXTURE_2D, crateTex);

	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexImage2D(GL_TEXTURE_2D, 0, 3, FreeImage_GetWidth(dib), FreeImage_GetHeight(dib), 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, FreeImage_GetBits(dib));
}

void init()
{
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	glShadeModel(GL_SMOOTH);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);
	glEnable(GL_COLOR_MATERIAL);
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
	glEnable(GL_LIGHTING);
	glEnable(GL_NORMALIZE);
	glEnable(GL_LIGHT0);
	glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
	glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, global_ambient);
	initTerrainAndSkybox();
	initParticle();
	createCrateTex("data/Crate.bmp");
	initCG();
	InitNx();
	createModel();
}

void reshape(int w, int h) // reshape function
{
	glViewport(0, 0, (GLsizei)w, (GLsizei)h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(60.0f, (GLfloat)w / (GLfloat)h, 0.1f, 10000.0f);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

void playMusic() // play music
{
	FMOD_RESULT result;
	FMOD::System *system;
	result = FMOD::System_Create(&system); // Create the main system object.
	if (result != FMOD_OK)
	{
		printf("FMOD error! (%d) %s\n", result, FMOD_ErrorString(result));
		exit(-1);
	}
	result = system->init(100, FMOD_INIT_NORMAL, 0); // Initialize FMOD.
	if (result != FMOD_OK)
	{
		printf("FMOD error! (%d) %s\n", result, FMOD_ErrorString(result));
		exit(-1);
	}
	FMOD::Sound *sound;
	result = system->createSound("data/kiss_the_rain.mp3", FMOD_LOOP_NORMAL | FMOD_2D | FMOD_HARDWARE, 0, &sound);
	if (result != FMOD_OK)
	{
		printf("FMOD error! (%d) %s\n", result, FMOD_ErrorString(result));
		exit(-1);
	}
	FMOD::Channel *channel;
	result = system->playSound(FMOD_CHANNEL_FREE, sound, false, &channel);
	if (result != FMOD_OK)
	{
		printf("FMOD error! (%d) %s\n", result, FMOD_ErrorString(result));
		exit(-1);
	}
}

void keyboard(unsigned char key, int x, int y)
{
	if (win == true) return;
	float vectorX = centerX - eyeX;
	float vectorY = centerY - eyeY;
	float vectorZ = centerZ - eyeZ;
	if (key == 'w' || key == 's' || key == 'a' || key == 'd')
	{
		if (key == 'w')
		{
			eyeX += vectorX * StepSize;
			eyeZ += vectorZ * StepSize;
			centerX += vectorX * StepSize;
			centerZ += vectorZ * StepSize;		
		}
		else if (key == 's')
		{
			eyeX -= vectorX * StepSize;
			eyeZ -= vectorZ * StepSize;
			centerX -= vectorX * StepSize;
			centerZ -= vectorZ * StepSize;
		}
		else if (key == 'a')
		{
			leftRightRotation += 2.5;
			eyeX = -vectorX * cos(3.142 / 72) - vectorZ * sin(3.142 / 72) + centerX;
			eyeZ = -vectorZ * cos(3.142 / 72) + vectorX * sin(3.142 / 72) + centerZ;
		}
		else if (key == 'd')
		{
			leftRightRotation -= 2.5;
			eyeX = -vectorX * cos(-3.142 / 72) - vectorZ * sin(-3.142 / 72) + centerX;
			eyeZ = -vectorZ * cos(-3.142 / 72) + vectorX * sin(-3.142 / 72) + centerZ;
		}

		glutPostRedisplay();
	}
	else if (key == ' ')
	{
		eyeX = 0.0f; // eyeX
		eyeY = 100.0f; // eyeY
		eyeZ = 1.0f; // eyeZ
		centerX = 0.0f; // centerX
		centerY = 100.0f; // centerY
		centerZ = 0.0f; // centerZ
		leftRightRotation = 0;
		glutPostRedisplay();
	}
	else if (key == ',')
	{
		pModelChar->executeAction(0);
	}
	else if (key == '.')
	{
		pModelChar->executeAction(1);
	}
	else if (key == '/')
	{
		processedLod = !processedLod;
	}
}

void showParticle()
{
	glEnable(GL_BLEND);									// Enable Blending
	glBlendFunc(GL_SRC_ALPHA,GL_ONE);					// Type Of Blending To Perform
	glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);	// Really Nice Perspective Calculations
	glHint(GL_POINT_SMOOTH_HINT,GL_NICEST);				// Really Nice Point Smoothing

	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, particleTex);
	for (loop=0;loop<MAX_PARTICLES;loop++)					// Loop Through All The Particles
	{
		if (particle[loop].active)							// If The Particle Is Active
		{
			float x=particle[loop].x;						// Grab Our Particle X Position
			float y=particle[loop].y;						// Grab Our Particle Y Position
			float z=particle[loop].z+zoom;					// Particle Z Pos + Zoom

			// Draw The Particle Using Our RGB Values, Fade The Particle Based On It's Life
			glColor4f(particle[loop].r,particle[loop].g,particle[loop].b,particle[loop].life);

			glBegin(GL_TRIANGLE_STRIP);						// Build Quad From A Triangle Strip
			glTexCoord2d(1,1); glVertex3f(x+0.5f,y+0.5f,z); // Top Right
			glTexCoord2d(0,1); glVertex3f(x-0.5f,y+0.5f,z); // Top Left
			glTexCoord2d(1,0); glVertex3f(x+0.5f,y-0.5f,z); // Bottom Right
			glTexCoord2d(0,0); glVertex3f(x-0.5f,y-0.5f,z); // Bottom Left
			glEnd();										// Done Building Triangle Strip

			particle[loop].x+=particle[loop].xi/(slowdown*100);// Move On The X Axis By X Speed
			particle[loop].y+=particle[loop].yi/(slowdown*100);// Move On The Y Axis By Y Speed
			particle[loop].z+=particle[loop].zi/(slowdown*100);// Move On The Z Axis By Z Speed

			particle[loop].xi+=particle[loop].xg;			// Take Pull On X Axis Into Account
			particle[loop].yi+=particle[loop].yg;			// Take Pull On Y Axis Into Account
			particle[loop].zi+=particle[loop].zg;			// Take Pull On Z Axis Into Account
			particle[loop].life-=particle[loop].fade;		// Reduce Particles Life By 'Fade'

			if (particle[loop].life<0.0f)					// If Particle Is Burned Out
			{
				particle[loop].life=1.0f;					// Give It New Life
				particle[loop].fade=float(rand()%100)/1000.0f+0.003f;	// Random Fade Value
				particle[loop].x=0.0f;						// Center On X Axis
				particle[loop].y=0.0f;						// Center On Y Axis
				particle[loop].z=0.0f;						// Center On Z Axis
				particle[loop].xi=xspeed+float((rand()%60)-32.0f);	// X Axis Speed And Direction
				particle[loop].yi=yspeed+float((rand()%60)-30.0f);	// Y Axis Speed And Direction
				particle[loop].zi=float((rand()%60)-30.0f);	// Z Axis Speed And Direction
				particle[loop].r=colors[col][0];			// Select Red From Color Table
				particle[loop].g=colors[col][1];			// Select Green From Color Table
				particle[loop].b=colors[col][2];			// Select Blue From Color Table
			}
		}
	}
	glDisable(GL_TEXTURE_2D);
	glDisable(GL_BLEND);
}

void showFigure()
{
	glDisable(GL_COLOR_MATERIAL);
	glPushMatrix();
	glTranslatef(centerX, centerY, centerZ);
	glRotatef(leftRightRotation, 0.0f, 1.0f, 0.0f);
	glTranslatef(0.0f, -0.2f, 0.0f);
	glScalef(0.003f, 0.003f, 0.003f);
	glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
	glRotatef(180.0f, 0.0f, 1.0f, 0.0f);

	unsigned int tick = Tick::getTick();
	pModelChar->onUpdate(double(tick - lastTick) / 1000);
	pModelChar->onRender();
	glPopMatrix();

	glPushMatrix();
	glTranslatef(skeletonX, skeletonY, skeletonZ);
	glScalef(0.3f, 0.3f, 0.3f);
	glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
	glRotatef(180.0f, 0.0f, 1.0f, 0.0f);
	pModelSkel->onUpdate(double(tick - lastTick) / 1000);
	pModelSkel->onRender();
	glPopMatrix();
	lastTick = tick;
}

void display()
{
	if (win == true)
	{
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		gluLookAt(0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambient);
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse);
		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
		glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
		glEnable(GL_NORMALIZE);
		glColor3f(0.0f, 1.0f, 1.0f);
		
		glutSolidTeapot(2);

		glDisable(GL_LIGHTING);
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluOrtho2D(0.0, 100.0, 0.0, 100.0);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		glColor3f(1, 1, 1);
		glRasterPos2i(5, 90);
		std::string text = "You win!";
		for(int i = 0; i < text.size(); i++)
			glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, text[i]);

		glFlush();
		glutSwapBuffers();
	}
	else
	{
		double x = double(centerX + 1000) / 2000 * (image->width - 1);
		double y = (1 - double(centerZ + 1000) / 2000) * (image->height - 1);
		int intx = x;
		int inty = y;
		int height = double(getHeight(intx, inty) * (1 - (x - intx)) * (1 - (y - inty))
					+ getHeight(intx, inty + 1) * (1 - (x - intx)) * (y - inty)
					+ getHeight(intx + 1, inty) * (x - intx) * (1 - (y - inty))
					+ getHeight(intx + 1, inty + 1) * (x - intx) * (y - inty)) / 255 / 5 * 1000;
		centerY = height + 2;
		eyeY = height + 2;

		double xs = double(skeletonX + 1000) / 2000 * (image->width - 1);
		double ys = (1 - double(skeletonZ + 1000) / 2000) * (image->height - 1);
		int intxs = xs;
		int intys = ys;
		int heights = double(getHeight(intxs, intys) * (1 - (xs - intxs)) * (1 - (ys - intys))
			+ getHeight(intxs, intys + 1) * (1 - (xs - intxs)) * (ys - intys)
			+ getHeight(intxs + 1, intys) * (xs - intxs) * (1 - (ys - intys))
			+ getHeight(intxs + 1, intys + 1) * (xs - intxs) * (ys - intys)) / 255 / 5 * 1000;
		skeletonY = heights + 2;

		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		gluLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, 0.0, 1.0, 0.0);

		// define material
		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambient);
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse);
		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
		glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
		glEnable(GL_NORMALIZE);

		// draw skybox
		drawSkybox();

		// draw particle
		glEnable(GL_COLOR_MATERIAL);
		glPushMatrix();
		glTranslatef(centerX, centerY, centerZ);
	
		for (int i = 0; i < 12; i++)
		{
			glRotatef(30.0f, 0.0f, 1.0f, 0.0f);
			glPushMatrix();
			glRotatef(60.0f, 1.0f, 0.0f, 0.0f);
			showParticle();
			glPopMatrix();
		}
		glPopMatrix();

		glPushMatrix();
		showFigure();
		glPopMatrix();

		glEnable(GL_DEPTH_TEST);
		glEnable(GL_COLOR_MATERIAL);
		glEnable(GL_LIGHTING);
		glEnable(GL_NORMALIZE);
		// enable lighting
		glEnable(GL_LIGHT0);
		glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient);
		glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
		glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
		glLightModelfv(GL_LIGHT_MODEL_AMBIENT, global_ambient);

		gScene->fetchResults(NX_RIGID_BODY_FINISHED);
		gScene->simulate(1/5.0f);
		gScene->flushStream();
		int nbActors = gScene->getNbActors();
		NxActor** actors = gScene->getActors();
		while(nbActors--)
		{
			NxActor* actor = *actors++;
			if(!actor->userData) continue;
			glPushMatrix();
			float glmat[16];
			actor->getGlobalPose().getColumnMajor44(glmat);
			glMultMatrixf(glmat);
			//glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
			if (!strcmp(actor->getName(), "cube"))
			{
				glEnable(GL_TEXTURE_2D);
				glBindTexture(GL_TEXTURE_2D, crateTex);
				int val = int(actor->userData);
				glPushMatrix();
				glScalef(val, val, val);
				glBegin(GL_QUADS);
				// Front Face
				glNormal3f( 0.0f, 0.0f, 1.0f);					// Normal Pointing Towards Viewer
				glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);	// Point 1 (Front)
				glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);	// Point 2 (Front)
				glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);	// Point 3 (Front)
				glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);	// Point 4 (Front)
				// Back Face
				glNormal3f( 0.0f, 0.0f,-1.0f);					// Normal Pointing Away From Viewer
				glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);	// Point 1 (Back)
				glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);	// Point 2 (Back)
				glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);	// Point 3 (Back)
				glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);	// Point 4 (Back)
				// Top Face
				glNormal3f( 0.0f, 1.0f, 0.0f);					// Normal Pointing Up
				glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);	// Point 1 (Top)
				glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,  1.0f,  1.0f);	// Point 2 (Top)
				glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,  1.0f,  1.0f);	// Point 3 (Top)
				glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);	// Point 4 (Top)
				// Bottom Face
				glNormal3f( 0.0f,-1.0f, 0.0f);					// Normal Pointing Down
				glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);	// Point 1 (Bottom)
				glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);	// Point 2 (Bottom)
				glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);	// Point 3 (Bottom)
				glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);	// Point 4 (Bottom)
				// Right face
				glNormal3f( 1.0f, 0.0f, 0.0f);					// Normal Pointing Right
				glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);	// Point 1 (Right)
				glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);	// Point 2 (Right)
				glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);	// Point 3 (Right)
				glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);	// Point 4 (Right)
				// Left Face
				glNormal3f(-1.0f, 0.0f, 0.0f);					// Normal Pointing Left
				glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);	// Point 1 (Left)
				glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);	// Point 2 (Left)
				glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);	// Point 3 (Left)
				glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);	// Point 4 (Left)
				glEnd();										// Done Drawing Quads
				glPopMatrix();
				glDisable(GL_TEXTURE_2D);
			}
			glPopMatrix();
		}

		glPushMatrix();
		glTranslatef(0.0f, 32.0f, -50.0f);
		glScalef(3, 3, 3);
		cgDisplay();
		glPopMatrix();

		glDisable(GL_LIGHTING);
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluOrtho2D(0.0, 100.0, 0.0, 100.0);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		glColor3f(1, 1, 1);
		glRasterPos2i(5, 90);
		std::string text;
		if (energy == 3) text = "Energy: 3";
		else if (energy == 2) text = "Energy: 2";
		else if (energy == 1) text = "Energy: 1";
		else text = "You turned into a ghost";
		for(int i = 0; i < text.size(); i++)
			glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, text[i]);

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(60.0f, (GLfloat)500 / (GLfloat)500, 0.1f, 10000.0f);
		glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
		glShadeModel(GL_SMOOTH);
		glEnable(GL_DEPTH_TEST);
		glDepthFunc(GL_LEQUAL);
		glEnable(GL_COLOR_MATERIAL);
		glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
		glEnable(GL_LIGHTING);
		glEnable(GL_NORMALIZE);
		glEnable(GL_LIGHT0);
		glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient);
		glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
		glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
		glLightModelfv(GL_LIGHT_MODEL_AMBIENT, global_ambient);

		glFlush();
		glutSwapBuffers();
	}
}

void idle()
{
	if (win == false)
	{
		if (delay > 25) delay = 0;		// Reset The Rainbow Color Cycling Delay
		col++;							// Change The Particle Color
		if (col>11)	col=0;				// If Color Is To High Reset It
		delay++;

		double distancesq = sqrt(double((centerX - skeletonX) * (centerX - skeletonX) + (centerZ - skeletonZ) * (centerZ - skeletonZ)));
		if (distancesq < 180)
		{
			skeletonX += (centerX - skeletonX) / distancesq / 2 * StepSize;
			skeletonZ += (centerZ - skeletonZ) / distancesq / 2 * StepSize;
		}

		if (distancesq < 50)
			energy--;
			

		NxVec3 vec = gScene->getActors()[1]->getGlobalPosition();
		if (vec.distance(NxVec3(centerX, centerY, centerZ)) < 3)
			win = true;

		glutPostRedisplay();
	}
}

int main(int argc, char** argv)
{
	playMusic();
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(500, 500);
	glutInitWindowPosition(100, 100);
	glutCreateWindow("RPG Game By Tracy Zhou");
	init();
	InitNx();
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);
	glutIdleFunc(idle);
	
	glutMainLoop();
	if(gPhysicsSDK && gScene) gPhysicsSDK->releaseScene(*gScene);
	gPhysicsSDK->release();
	return 0;
}